package com.pine.dcts.config;

import com.alibaba.datax.common.constant.PluginType;
import com.alibaba.datax.core.util.container.JarLoader;
import com.pine.dcts.common.DctsConstant;
import com.pine.dcts.common.DctsException;
import com.pine.dcts.entity.PluginInfo;
import com.pine.dcts.service.PluginInfoService;
import com.pine.dcts.utils.SecurityUtils;
import com.pine.dcts.utils.ZipUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.pine.dcts.utils.FileUtils.getAbsPath;
import static com.pine.dcts.utils.LoaderUtils.generatePluginKey;

/**
 *  插件加载类
 */
@Configuration
@Slf4j
@Transactional
@Order(10)
public class PluginManager implements ApplicationRunner {

    @Autowired
    private PluginInfoService pluginInfoService;
    @Autowired
    private DctsPropertis corePropertis;

    /**
     * jarLoader的缓冲
     */
    public static final Map<String, JarLoader> jarLoaderCenter = new HashMap<>();


    /**
     *  插件的注册中心
     */
    public static final Map<String, com.alibaba.datax.common.util.Configuration>
            pluginRegisterCenter = new HashMap<>();
    @Override
    public void run(ApplicationArguments args) throws Exception {

        loadPlugins();

    }

    public void disabled(Integer id){
        PluginInfo pluginInfo = pluginInfoService.getById(id);
        if(pluginInfo==null){
            throw new DctsException("插件不存在,停用失败");
        }
        if(DctsConstant.UNINSTALL.equals(pluginInfo.getStatus())){
            throw new DctsException("插件已卸载,停用失败");
        }
        if(DctsConstant.NO.equals(pluginInfo.getStatus())){
            throw new DctsException("插件已停用");
        }
        pluginInfo.setStatus(DctsConstant.NO);
        pluginInfoService.updateById(pluginInfo);
        pluginRegisterCenter.remove(  generatePluginKey(
                PluginType.valueOf(pluginInfo.getType().toUpperCase()),
                pluginInfo.getName()));
        jarLoaderCenter.remove( generatePluginKey(
                PluginType.valueOf(pluginInfo.getType().toUpperCase()),
                pluginInfo.getName()));

    }

    public void enabled(Integer id){
        PluginInfo pluginInfo = pluginInfoService.getById(id);
        if(pluginInfo==null){
            throw new DctsException("插件不存在,启用失败");
        }
        if(DctsConstant.UNINSTALL.equals(pluginInfo.getStatus())){
            throw new DctsException("插件已卸载,启用失败");
        }
        if(DctsConstant.YES.equals(pluginInfo.getStatus())){
            throw new DctsException("插件已启用");
        }
        pluginInfo.setStatus(DctsConstant.YES);
        pluginInfo.setEditTime(new Date());
        pluginInfo.setEditBy(Integer.valueOf(SecurityUtils.getUserId()));
        pluginInfoService.updateById(pluginInfo);
        pluginRegisterCenter.remove(  generatePluginKey(
                PluginType.valueOf(pluginInfo.getType().toUpperCase()),
                pluginInfo.getName()));
        jarLoaderCenter.remove( generatePluginKey(
                PluginType.valueOf(pluginInfo.getType().toUpperCase()),
                pluginInfo.getName()));
        loadOnePlugin("",pluginInfo);
    }

    public void reduction( Integer id){
        PluginInfo pluginInfo = pluginInfoService.getById(id);
        if(pluginInfo==null){
            throw new DctsException("插件不存在,还原失败");
        }
        if(StringUtils.isBlank(pluginInfo.getPreVersion())){
            throw new DctsException("上个版本插件未备份或者不存在,无法还原");
        }
        File resPath = new File(StringUtils.join(new
                String[]{corePropertis.getTemp(), pluginInfo.getType(),pluginInfo.getName(),
                pluginInfo.getPreVersion()}, File.separator));
        if(!resPath.exists()){
            throw new DctsException(String.format("插件信息还原错误,插件类型[%s],插件名称[%s]",
                    pluginInfo.getType(),pluginInfo.getName()));
        }
        String pluginPath = getAbsPath(corePropertis.getPlugins());
        File destPath = new File(StringUtils.join(new
                String[]{pluginPath, pluginInfo.getType(),pluginInfo.getName()}, File.separator));
        try {
            FileUtils.copyDirectory(resPath,destPath);
        } catch (IOException e) {
            log.error(String.format("插件信息还原错误,插件类型[%s],插件名称[%s]",
                    pluginInfo.getType(),pluginInfo.getName()), e);
            throw new DctsException(String.format("插件信息还原错误,插件类型[%s],插件名称[%s]",
                    pluginInfo.getType(),pluginInfo.getName()));
        }

        pluginInfo.setVersion(pluginInfo.getPreVersion());
        pluginInfo.setPreVersion(null);
        pluginInfo.setStatus(DctsConstant.YES);
        pluginInfo.setEditTime(new Date());
        pluginInfo.setEditBy(Integer.valueOf(SecurityUtils.getUserId()));
        this.pluginInfoService.updateById(pluginInfo);
        loadOnePlugin(pluginPath,pluginInfo);

    }

    public void backup(  PluginInfo pluginInfo){
        String pluginPath = getAbsPath(corePropertis.getPlugins());
        File resoucePath = new File(StringUtils.join(new
                String[]{pluginPath, pluginInfo.getType(),pluginInfo.getName()}, File.separator));
        File destPath = new File(StringUtils.join(new
                String[]{corePropertis.getTemp(), pluginInfo.getType(),pluginInfo.getName(),
                pluginInfo.getVersion()}, File.separator));
        if(!destPath.exists()){
            destPath.mkdirs();
        }
        try {
            FileUtils.copyDirectory(resoucePath,destPath);
        } catch (IOException e) {
            log.error(String.format("插件信息备份错误,插件类型[%s],插件名称[%s]",
                    pluginInfo.getType(),pluginInfo.getName()), e);
            throw new DctsException(String.format("插件信息备份错误,插件类型[%s],插件名称[%s]",
                    pluginInfo.getType(),pluginInfo.getName()));
        }
    }

    public void validateInstall(PluginInfo pluginInfo){
        PluginInfo pluginInfo1 =   pluginInfoService.selectOneByNameAndType(
                pluginInfo.getName(),pluginInfo.getType());
        if(pluginInfo1!=null&& pluginInfo1.getStatus().equals(DctsConstant.YES)){
            throw new DctsException(String.format("该插件已安装,请勿重复安装",
                    pluginInfo.getType(),pluginInfo.getName()));
        }
        if(pluginInfo1!=null&& pluginInfo1.getStatus().equals(DctsConstant.NO)){
            throw new DctsException(String.format("该插件已停用,请启用该插件",
                    pluginInfo.getType(),pluginInfo.getName()));
        }
    }

    /**
     *  插件升级
     * @param id
     * @param version
     * @param zipFile
     */
    public void upgrade(Integer id,String version,File zipFile){
        PluginInfo pluginInfo = pluginInfoService.getById(id);
        if(pluginInfo==null){
            throw new DctsException("插件信息不能为空,升级失败");
        }
        if(pluginInfo.getVersion().equalsIgnoreCase(version)){
            throw new DctsException("该插件版本已存在");
        }
        pluginInfo.setVersion(version);
        //卸载插件
        unInstall(pluginInfo);
        install(pluginInfo,zipFile);
    }

    /**
     *  插件卸载
     * @param pluginInfo
     */
    public void unInstall( PluginInfo pluginInfo){

        //先更新数据库插件信息，移除插件注册信息和jarclassloader信息
        pluginInfo.setStatus(DctsConstant.UNINSTALL);
        pluginInfo.setPreVersion(pluginInfo.getVersion());
        pluginInfo.setEditTime(new Date());
        pluginInfo.setEditBy(Integer.valueOf(SecurityUtils.getUserId()));
        pluginInfoService.updateById(pluginInfo);

        pluginRegisterCenter.remove(  generatePluginKey(
                PluginType.valueOf(pluginInfo.getType().toUpperCase()),
                pluginInfo.getName()));
        jarLoaderCenter.remove( generatePluginKey(
                PluginType.valueOf(pluginInfo.getType().toUpperCase()),
                pluginInfo.getName()));
        //备份插件信息，用于进行还原版本处理
        backup(pluginInfo);
        String pluginPath = getAbsPath(corePropertis.getPlugins());
        File pluginFilePath = new File(StringUtils.join(new
                String[]{pluginPath, pluginInfo.getType(),pluginInfo.getName()}, File.separator));
        try {
            //删除插件包
            FileUtils.forceDelete(pluginFilePath);
        } catch (IOException ex) {
            log.warn("删除插件异常,插件所在路径为：" + pluginFilePath.getAbsolutePath(), ex);
        }
    }


    public void unInstall(Integer id){
        PluginInfo pluginInfo = pluginInfoService.getById(id);
        if(pluginInfo==null){
            throw new DctsException("插件信息不存在,卸载失败");
        }
        if(DctsConstant.UNINSTALL.equals(pluginInfo.getStatus())){
            throw new DctsException("插件已卸载,请勿重复卸载");
        }
        unInstall(pluginInfo);
    }

    /**
     *  进行插件安装
     * @param pluginInfo
     * @param zipFile
     */
    public void install(PluginInfo pluginInfo,File zipFile){
        if(pluginInfo==null){
            throw new DctsException("插件信息不能为空");
        }
        if(this.corePropertis==null|| StringUtils.isEmpty(corePropertis.getPlugins())){
            throw new DctsException("需要加载的插件配置文件不能为空");
        }
        String pluginPath = getAbsPath(corePropertis.getPlugins());
        File file = new File(pluginPath);
        if(!file.exists()|| !file.isDirectory()){
            throw new DctsException("需要加载的插件地址错误");
        }
        File[] files=file.listFiles();
        if(files.length==0){
            throw new DctsException("需要加载的插件为空");
        }
        PluginInfo pluginInfo1 =   pluginInfoService.selectOneByNameAndType(
                pluginInfo.getName(),pluginInfo.getType());
        if(pluginInfo1!=null){
            pluginInfo1.setStatus(DctsConstant.YES);
            pluginInfo1.setPreVersion(pluginInfo1.getVersion());
            pluginInfo1.setVersion(pluginInfo.getVersion());
            pluginInfo.setEditTime(new Date());
            pluginInfo.setEditBy(Integer.valueOf(SecurityUtils.getUserId()));
            pluginInfoService.updateById(pluginInfo1);
        }else{
            pluginInfo.setStatus(DctsConstant.YES);
            pluginInfo.setInstallBy(Integer.valueOf(SecurityUtils.getUserId()));
            pluginInfo.setInstallTime(new Date());
            pluginInfoService.save(pluginInfo);
        }
        File pluginFilePath = new File(StringUtils.join(new
                String[]{pluginPath, pluginInfo.getType(),pluginInfo.getName()}, File.separator));
        if(ZipUtils.unZip(zipFile,pluginFilePath.getAbsolutePath())){
            loadOnePlugin(pluginPath,pluginInfo);
        }
    }

    /**
     *  加载全部插件
     */
    public void loadPlugins(){
        if(this.corePropertis==null|| StringUtils.isEmpty(corePropertis.getPlugins())){
            throw new DctsException("需要加载的插件配置文件不能为空");
        }
        String pluginPath = getAbsPath(corePropertis.getPlugins());
        File file = new File(pluginPath);
        if(!file.exists()|| !file.isDirectory()){
            throw new DctsException("需要加载的插件地址错误");
        }
        File[] files=file.listFiles();
        if(files.length==0){
            throw new DctsException("需要加载的插件为空");
        }
        List<PluginInfo>  pluginInfos = this.pluginInfoService.selectEnabledList();
        if(CollectionUtils.isEmpty(pluginInfos)){
            throw new DctsException("需要加载的插件为空");
        }
        for(PluginInfo pluginInfo:pluginInfos){
            loadOnePlugin(pluginPath,pluginInfo);
        }
        log.info("插件已全部加载完成");

    }

    /**
     *  可以根据单个插件信息加载插件
     * @param basePath
     * @param pluginInfo
     */
    public void loadOnePlugin(String basePath,PluginInfo pluginInfo){
        String pluginPath =basePath;
        if(StringUtils.isEmpty(basePath)){
            if(this.corePropertis==null|| StringUtils.isEmpty(corePropertis.getPlugins())){
                throw new DctsException("需要加载的插件配置文件不能为空");
            }
            pluginPath = getAbsPath(corePropertis.getPlugins());
            File file = new File(pluginPath);
            if(!file.exists()|| !file.isDirectory()){
                throw new DctsException("需要加载的插件地址错误");
            }
            File[] files=file.listFiles();
            if(files.length==0){
                throw new DctsException("需要加载的插件为空");
            }
        }
        File pluginFilePath = new File(StringUtils.join(new
                String[]{pluginPath, pluginInfo.getType(),pluginInfo.getName()}, File.separator));
        if(!pluginFilePath.exists()|| !pluginFilePath.isDirectory()){
            throw new DctsException(String.format("需要加载的插件不存在，类型为[%s]，插件名[%s]",
                    pluginInfo.getType(),pluginFilePath.getName()));
        }
        JarLoader jarLoader  = new JarLoader(new String[]{pluginFilePath.getAbsolutePath()});
        if(jarLoader==null){
            throw new DctsException(String.format("需要加载的插件不存在，类型为[%s]，插件名[%s]",
                    pluginInfo.getType(),pluginFilePath.getName()));
        }

            jarLoaderCenter.put(
                    generatePluginKey(
                            PluginType.valueOf(pluginInfo.getType().toUpperCase()),
                            pluginInfo.getName()),jarLoader);

        String pj = pluginFilePath.getAbsolutePath()+File.separator+"plugin.json";
        pluginRegisterCenter.put(
                generatePluginKey(
                        PluginType.valueOf(pluginInfo.getType().toUpperCase()),
                        pluginInfo.getName()),
                com.alibaba.datax.common.util.Configuration.from(DctsConfigParser.getJobContent(pj)));
        log.info(String.format("插件加载完成，类型为[%s]，插件名[%s]",  pluginInfo.getType(),pluginFilePath.getName()));
    }
}
